//===============================================================================
// Microsoft patterns & practices Enterprise Library Contribution
// Validation Application Block
//===============================================================================
// Code file original author: Martin Lapierre, http://devinstinct.com
//===============================================================================

using Microsoft.Practices.EnterpriseLibrary.Validation;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
using EntLibContrib.Validation.Validators;
using EntLibContrib.Validation.Properties;

namespace EntLibContrib.Validation
{
	/// <summary>
	/// Represents an argument validation class to 
	/// test method pre-conditions and post-conditions.
	/// </summary>
	public static class ArgumentValidation
	{
		#region Public Methods
		/// <summary>
		/// Validates an argument using the provided validators and throws
		/// an <see cref="ArgumentValidationException"/> containing the parameter value if the validation fails.
		/// </summary>
		/// <param name="parameterName">Name of the parameter.</param>
		/// <param name="parameter">The parameter.</param>
		/// <param name="validators">The validators to use to test the argument.</param>
		public static void Validate(string parameterName, object parameter, params Validator[] validators)
		{
			// Validate pre-conditions.
			// Done in overlead.
			Validate(null, parameterName, parameter, false, validators);
		}

		/// <summary>
		/// Validates an argument using the provided validators and throws
		/// an <see cref="ArgumentValidationException"/> optionally containing the parameter value if the validation fails.
		/// </summary>
		/// <param name="parameterName">Name of the parameter.</param>
		/// <param name="parameter">The parameter.</param>
		/// <param name="safeException">True if the parameter value shouldn't be attached to the exception, false otherwise.</param>
		/// <param name="validators">The validators to use to test the argument.</param>
		public static void Validate(string parameterName, object parameter, bool safeException, params Validator[] validators)
		{
			// Validate pre-conditions.
			// Done in overlead.
			Validate(null, parameterName, parameter, safeException, validators);
		}

		/// <summary>
		/// Validates an argument using the provided validators and throws
		/// an <see cref="ArgumentValidationException"/> optionally containing the parameter value if the validation fails.
		/// </summary>
		/// <param name="message">The error message that explains the reason for the exception.</param>
		/// <param name="parameterName">Name of the parameter.</param>
		/// <param name="parameter">The parameter.</param>
		/// <param name="safeException">True if the parameter value shouldn't be attached to the exception, false otherwise.</param>
		/// <param name="validators">The validators to use to test the argument.</param>
		public static void Validate(string message, string parameterName, object parameter, bool safeException, params Validator[] validators)
		{
			// Validate pre-conditions.
			// "message" can be null.
			// "param" can be null.
			InnerValidate(null, "paramName", parameterName, false, DefaultValidators.StringNotNullNorEmptyValidator);
			InnerValidate(null, "validators", validators, false, DefaultValidators.CollectionNotNullNorEmptyValidator);

			// Validate the provided parameter.
			InnerValidate(message, parameterName, parameter, safeException, validators);
		}
		#endregion

		#region Private Methods
		/// <summary>
		/// Contains the validation process logic.
		/// </summary>
		/// <param name="message">The error message that explains the reason for the exception.</param>
		/// <param name="paramName">The name of the argument.</param>
		/// <param name="param">The argument.</param>
		/// <param name="safeException">True if the parameter value shouldn't be attached to the exception, false otherwise.</param>
		/// <param name="validators">The validators to use to test the argument.</param>
		private static void InnerValidate(string message, string paramName, object param, bool safeException, params Validator[] validators)
		{
			// Validate pre-conditions.
			// Internal class call: arguments assumed to be valid.
			// "message" can be null.
			// "param" can be null.
			Validator mainValidator = null;
			ValidationResults validationResults = null;

			if (validators.Length == 1)
				mainValidator = validators[0];
			else
				mainValidator = new AndCompositeValidator(validators);

			validationResults = mainValidator.Validate(param);
			if (!validationResults.IsValid)
			{
				if (message == null)
					message = Resources.ArgumentValidationDefaultMessage;

				if (safeException) // Do not propagate parameter in exception.
					throw new ArgumentValidationException(message, paramName, validationResults);
				else // Propagate parameter in exception.
					throw new ArgumentValidationException(message, paramName, param, validationResults);
			}
		}
		#endregion
	}
}
